Duration, Date and Time

Duration

The library SysTimeCore supports functions for handling the difference (delta) between two points in time (duration) provided by the CODESYS runtime system.

The SysTime alias type is defined to handle ULINT values (0 - 18.446.744.073.709.551.615).

xOK := (SIZEOF(SysTime) = 8 (* Bytes *)) AND (SIZEOF(ULINT) = 8 (* Bytes *));

Note

In the following examples the prefix st was selected for variables of type SysTime.

See: Naming Conventions for variables of type UDINT, ULINT, TIME and LTIME.

SysTimeGetMs

Returns a timer tick value (point in time) of type UDINT in a resolution of one millisecond ([ms]).

This results in a value range of 4.294.967.296ms ≈ 4.294.967s ≈ 7.582m ≈ 1.193h ≈ 49d

Note

The value range of the data type TIME corresponds to the value range of the data type UDINT with a resolution of one milli­second.

Note

Instead of the function SysTimeGetMs, the operator TIME() can also be used.

VAR
   tDelta, tStart, tEnd : TIME;
END_VAR

tStart := TO_TIME(SysTimeGetMs());
(* ... lengthy operation ... *)
tEnd := TO_TIME(SysTimeGetMs());
tDelta := tEnd - tStart (* ms *);

The code example in the last section has the same effect as the code example in the next section.

tStart := TIME();
(* ... lengthy operation ... *)
tDelta := TIME() - tStart (* ms *);

SysTimeGetUs

Returns a timer tick value (point in time) of type SysTime in a resolution of one microsecond ([µs]).

This results in a value range of 18.446.744.073.709.551.615µs ≈ 18.446.744.073.709.551ms ≈ 18.446.744.073.709s ≈ 307.445.734.561m ≈ 5.124.095.576h ≈ 213.503.982d ≈ 599.730y

VAR
   stDelta, stStart, stEnd : SysTime;
END_VAR

SysTimeGetUs(stStart);
(* ... lengthy operation ... *)
SysTimeGetUs(stEnd);
stDelta := stEnd - stStart (* µs *);

SysTimeGetNs

Returns a timer tick value (point in time) of type SysTime in a resolution of one nanosecond ([ns]).

This results in a value range of 18.446.744.073.709.551.615ns ≈ 18.446.744.073.709.551µs ≈ 18.446.744.073.709ms ≈ 18.446.744.073s ≈ 307.445.734m ≈ 5.124.095h ≈ 213.503d ≈ 599y

Note

The value range of the data type LTIME corresponds to the value range of the data type SysTime and ULINT with a resolu­tion of one nanosecond.

Note

Instead of the function SysTimeGetNs, the operator LTIME() can also be used.

VAR
   ltDelta : LTIME;
   stStart, stEnd : SysTime;
END_VAR

SysTimeGetNs(stStart);
(* ... lengthy operation ... *)
SysTimeGetNs(stEnd);
ltDelta := TO_LTIME(stEnd - stStart) (* ns *);

The code example in the last section has the same effect as the code example in the next section.

VAR
   ltDelta, ltStart : LTIME;
END_VAR

ltStart := LTIME();
(* lengthy operation *)
ltDelta := LTIME() - ltStart (* ns *);

Timer

The handling of a time duration at the basis of the data types TIME and LTIME are also the kernel of the timer function blocks in the libraries standard (for TIME) and standard64 (for LTIME).

Example

PROGRAM TIMER
VAR
   tDelta : TIME := T#2s;
   T1 : TON;
   T2 : TP;
   T3 : TOF;
END_VAR

timer.png

Date and Time

Problems with Localtime

The major problem dealing with the localtime is that certain times of a day may occur twice in a year. For example, in the US/Eastern timezone on the last Sunday morning in October, the following sequence happens:

In fact, every instant between 01:00 and 02:00 occurs twice. This means that if a variable of type "TIME" is written in the "US/Eastern" time zone, it can no longer be decided whether it was written shortly before or after the transition from summer time to winter time.

The best and simplest solution is to stick with using UTC. Note that some other timezones are commonly thought of as the same (GMT, Green­wich, Universal, etc.). The definition of UTC is distinct from these other timezones, and they are not equivalent.

"UTC" is Coordinated Universal Time. It is a successor to, but distinct from, Greenwich Mean Time (GMT) and the various definitions of Universal Time. UTC is currently the worldwide standard for regulating clocks and time measurement.

All other timezones are defined relative to UTC, and include offsets like UTC+0800 - hours to add or subtract from UTC to derive the local time. No daylight saving time occurs in UTC, making it a useful timezone to perform date arithmetic without worrying about the confusion and ambi­guities caused by daylight saving time transitions, your country changing its timezone, or mobile applications that roam through multiple time­zones.

The SysTimeRTC Library provides a set of functions to deal with this issues and helps to handle variables of the types DATE, TIME_OF_DAY and DATE_AND_TIME correctly.

The Data Types

CODESYS is following the IEC 61131-3 standard and implements there­fore some data types:

Starting with data type DATE_AND_TIME, variables of the other data types can be assigned accordingly. A variable of type DATE_AND_TIME can be initialized with a current value with the help of the CODESYS runtime system's functions SysTimeRtcGet and SysTimeRTCConver­tUTCToLocal.

See: Naming Conventions for variables of type UDINT, DATE, TIME_OF_DAY and DATE_AND_TIME.

VAR
   udiUTC_DateAndTime : UDINT;
   udiLocal_DateAndTime : UDINT;

   Result : RTS_IEC_RESULT;

   dtNow : DATE_AND_TIME;
   todNow : TIME_OF_DAY;
   datNow : DATE;
END_VAR

udiUTC_DateAndTime := TO_UDINT(SysTimeRtcGet(Result)); // UDINT#1528268918
Result := SysTimeRTCConvertUTCToLocal(udiUTC_DateAndTime, udiLocal_DateAndTime); // UDINT#1528276118

dtNow := TO_DT(udiLocal_DateAndTime); // DT#2018-6-6-9:8:38
todNow := TO_TOD(dtNow); // TOD#9:8:38
datNow := TO_DATE(dtNow); // D#2018-6-6

The CODESYS runtime system specifies some additional data types:

Getting Date and Time (UTC)

The CODESYS runtime system provides the following functions for getting the current date and time in the UTC timezone:

Converting from UTC to Localtime

Note

The concept "local timezone" in the context of the CODESYS runtime system is based on the time zone witch the local controller is configured for. This is not always the time zone suitable for displaying the current time. Some times it is necessary to use the timezone which the CODESYS development environment is configured for. Some times there are other requirements to use a completely different time zone.

In order to meet these conflicting requirements, the util.library offers the option of using the functions for converting time and date data with a flexible specification of a freely select­able time zone.

The CODESYS runtime system provides the following functions for converting the current date and time from the UTC timezone to the local timezone:

Note

In the following examples the prefix std was selected for variables of type SysTimeDate.

VAR
   stUTC_Timestamp : SysTime;
   stLocal_TimeStamp : SysTime;
   stdNow : SysTimeDate;

   Result : RTS_IEC_RESULT;

   dtNow : DATE_AND_TIME;
   todNow : TIME_OF_DAY;
   datNow : DATE;
END_VAR

Result := SysTimeRtcHighResGet(stUTC_Timestamp); // ULINT#1528273494913
Result := SysTimeRtcConvertHighResToLocal(stUTC_Timestamp, stdNow);

// stdNow.wYear = UINT#2018
// stdNow.wMonth = UINT#6
// stdNowy.wDay = UINT#6
// stdNow.wHour = UINT#10
// stdNow.wMinute = UINT#24
// stdNow.wSecond = UINT#54
// stdNow.wMilliseconds = UINT#913
// stdNow.wDayOfWeek = UINT#3
// stdNow.wYday = UINT#157

Result := SysTimeRtcConvertDateToHighRes(stdNow, stLocal_TimeStamp); // ULINT#1528280694913

dtNow := TO_DT(stLocal_TimeStamp / 1000 (* ms *)); // DT#2018-6-6-10:24:54
todNow := TO_TOD(stLocal_TimeStamp MOD TO_ULINT(T#1D)); // TOD#10:24:54.913
datToday := TO_DATE(dtNow); // D#2018-6-6

Converting from Localtime to UTC

The CODESYS runtime system provides the following function for converting the current date and time from the local timezone to the UTC timezone:

VAR
   stdNew : SysTimeDate;
   stUTC_Timestamp : SysTime;
END_VAR

// stdNew.wYear = UINT#2018
// stdNew.wMonth = UINT#6
// stdNew.wDay = UINT#6
// stdNew.wHour = UINT#10
// stdNew.wMinute = UINT#24
// stdNew.wSecond = UINT#54
// stdNew.wMilliseconds = UINT#913

Result := SysTimeRtcConvertLocalToHighRes(stdNew, stUTC_Timestamp); // ULINT#1528273494913

// Writing a high resolution value of type ``SysTime`` to the realtime clock (RTC) device.
Result := SysTimeRtcHighResSet(stUTC_Timestamp);

Support from the Util.library

To handle the local time, it is neccecary to always specify the time and date and the time zone that is currently valid. This makes it possible to convert the local time to Coordinated Universal Time and vice versa. UTC is Coordinated Universal Time. It is a successor to, but distinct from, Greenwich Mean Time (GMT) and the various definitions of Universal Time. UTC is now the worldwide standard for regulating clocks and time measurement.

All other timezones are defined relative to UTC, and include offsets like UTC+0800 - hours to add or subtract from UTC to derive the local time. No daylight saving time occurs in UTC, making it a useful timezone to perform date arithmetic without worrying about the confusion and ambi­guities caused by daylight saving time transitions, your country changing its timezone, or mobile computers that roam through multiple timezones.

The following snippet exposes the definition of UTC and CentralEurope­Time/CentralEuropeSommerTime.

VAR_GLOBAL CONSTANT
   /// Coordinated Universal Time
    gc_tzTimeZoneUTC : TimeZone := (asgPeriod := [(sName:='UTC')]);

   /// Central Europe Time
   gc_tzTimeZoneCET : TimeZone :=
   (
       iBias := 60 (* T#1M => minutes *),
       asgPeriod := [
       ( (* (CEST -> CET) - Last Sunday in Oktober at 03:00:00.000 (CEST) *)
           sName:='CET',
            dtDate := (uiMonth := 10, eWeekday := WEEKDAY.SUNDAY, uiDay := 5, uiHour := 3)
       ),( (* (CET -> CEST) - Last Sunday in March at 02:00:00.000 (CET) *)
            sName := 'CEST',
            dtDate := (uiMonth := 3, eWeekday := WEEKDAY.SUNDAY, uiDay := 5, uiHour := 2),
           iBias := 60 (* T#1M => minutes *)
       )]
   );
END_VAR

Note

The Bias element represents the offset from the Coordinated Universal Time (UTC). This value is in minutes.

  • The offset growing positive in eastern direction starting from the prime meridian.

  • The offset is growing negative in western direction starting from the prime meridian.

TimeZones.png

See: List of UTC time offsets

With the data structure TimeZone it is possible to specify every timezone of the world and so the functions below can handle the local time conver­sion and the switching from standard to day light saving period's if necessary.

Example

With the following expressions the difference between the UTC time zone and an other timezone instance can be calculated.

iBiasUTC_Standard := gc_tzTimeZoneCET.iBias;
iBiasUTC_Daylight := gc_tzTimeZoneCET.iBias + gc_tzTimeZoneCET.asgPeriod[PERIOD.DAYLIGHT].iBias;

Note

The time zone which the current computer is configured for, is not always the time zone suitable for displaying the current time. Therefore in the respective application, the possibility should be provided, to be able to select the "correct" time zone that is suitable for the related output option (WebVisu, LogFiles, Email, ...).

In version 3.5.14.0 and higher, the Util library provides the following functions:

Conversion from UTC timestamp (e.g. from SysTimeRtcHighResGet) to the current period and the local date and time related to the TimeZone parameter.

FUNCTION PUBLIC LocalDateTime : ULINT
VAR_IN_OUT CONSTANT
   tzTimeZone : TimeZone;
END_VAR
VAR_INPUT
   uliDateTime : ULINT;
END_VAR
VAR_OUTPUT
   eErrorID : ERROR;
    ePeriod : PERIOD; (* 1 = ``PERIOD.STANDARD``, 2 = ``PERIOD.DAYLIGHT`` *)
END_VAR

Splitting a timestamp into the components of a point in.

FUNCTION PUBLIC SplitDateTime : ERROR
VAR_INPUT
   uliDateTime : ULINT;
END_VAR
VAR_OUTPUT
   uiYear : YEAR; (* 1970..2106 *)
   uiMonth : MONTH;
   uiDay : DAY;
   uiHour : HOUR;
   uiMinute : MINUTE;
   uiSecond : SECOND;
   uiMilliseconds : MILLISECOND;
   eWeekday : WEEKDAY;
END_VAR

Joining the components of a point in time to a timestamp.

FUNCTION PUBLIC JoinDateTime : ULINT
VAR_INPUT
   uiYear : YEAR; (* 1970..2106 *)
   uiMonth : MONTH;
   uiDay : DAY;
   uiHour : HOUR;
   uiMinute : MINUTE;
   uiSecond : SECOND;
   uiMilliseconds : MILLISECOND;
END_VAR
VAR_OUTPUT
   eErrorID : ERROR := ERROR.NO_ERROR;
END_VAR

Separates a timestamp in its IEC data typed parts.

FUNCTION PUBLIC SeparateDateTime : ERROR
VAR_INPUT
   uliDateTime : ULINT;
END_VAR
VAR_OUTPUT
   eWeekDay : WEEKDAY;
   datDate : DATE;
   todTime : TIME_OF_DAY;
END_VAR

Combines the IEC data typed parts to a timestamp.

FUNCTION PUBLIC CombineDateTime : ULINT
VAR_INPUT
   datDate : DATE;
   todTime : TIME_OF_DAY;
END_VAR
VAR_OUTPUT
   eErrorID : ERROR;
END_VAR

Calculates the appropriate value of the WEEKDAY enum that matches the parameter datDate.

FUNCTION PUBLIC DayOfWeek : WEEKDAY
VAR_INPUT
   datDate : DATE;
END_VAR
VAR_OUTPUT
   eErrorID : ERROR;
END_VAR

Checks whether the year (uiYear) passed represents a leap year.

FUNCTION PUBLIC IsLeapYear : BOOL
VAR_INPUT
   uiYear : YEAR; (* 1970..2106 *)
END_VAR
VAR_OUTPUT
   eErrorID : ERROR;
END_VAR